/**
 *  \file I3OCDStateInfo.cpp
 *  \brief I3OCDStateInfo class encapsulates i3 specific properties 
 *  associated with a particular connection in addition to the generic
 *  derived properties from OCDStateInfo.
*/

#include "i3_debug.h"
#include "OCD_include.h"


#include "I3OCDStateInfo.h"
#include "I3OverlayNetwork.h"
#include "i3CommonHeaders.h"
#include "I3TimerEvent.h"
#include "I3Trigger.h"
#include "I3Id.h"

extern "C" {
    #include "i3_id.h"
}

/**
  * This constructor is used to create an I3Virtual node not on receipt of a DNS request, 
  * but at other times, like a private trigger request was received.
  * @param overlay The overlay network associated with this virtual node.
  */
I3OCDStateInfo::I3OCDStateInfo(LayerHandle myHandle, OCI* oci, I3OverlayNetwork *overlay)
            : OCDStateInfo (myHandle, oci, overlay) {

	// zero out the various ids
	memset (contactId.x, 0, sizeof(ID));
	memset (prv_id.x, 0, sizeof(ID));

    this->overlay =  overlay;
    prvTriggerConfirmationTimer = NULL;
    remotePrivateId = NULL;
    remotePublicId = NULL;
    prv_cl_trig = NULL;
    numRemotePrivateIdRequests = 0;
    crashRecoveryInProgress = false;
    nameHashedFlag = false;
    localPrivTrigInsertedFlag = false;
    localPrivateTrigger = NULL;
    
    enableFastConnectionSetup = false;
}

/** Destructor */
I3OCDStateInfo::~I3OCDStateInfo() {

    I3_PRINT_DEBUG1 (DEBUG_LEVEL_SUPER, "Destructing i3 OCD state %p\n", this);

    I3OverlayNetwork *tmpI3Overlay = (I3OverlayNetwork *) ocd;
    tmpI3Overlay->destroyVN (this);

    if (remotePrivateId != NULL) {
        delete (remotePrivateId);
    }

    if (remotePublicId != NULL) {
        delete (remotePublicId);
    }

    if (prvTriggerConfirmationTimer != NULL) {
        prvTriggerConfirmationTimer->setDisabled();
        prvTriggerConfirmationTimer = NULL;
    }


    // delete the stae associated with the local private trigger
    if (localPrivateTrigger != NULL) {
        delete localPrivateTrigger;
        localPrivateTrigger = NULL;
    }
    //TODO
    //if (prv_cl_trig != NULL) {
    //    delete (prv_cl_trig);
    //}

}

void I3OCDStateInfo::getJournalRecord(char *buf) {


    char prv_id_buf[MAX_PRINT_BUF];
    char prv_trig_key_buf[MAX_PRINT_BUF];
    char remotePrivateId_buf[MAX_PRINT_BUF];
    char remotePublicId_buf[MAX_PRINT_BUF];
    
    sprintf_i3_id(prv_id_buf, &prv_id);
    sprintf_i3_key(prv_trig_key_buf, &prv_trig_key);
    
    if (remotePrivateId != NULL) {
        sprintf_i3_id(remotePrivateId_buf, &remotePrivateId->id);
    } else {
        sprintf(remotePrivateId_buf, "<<NULL>>");
    }

    if (remotePublicId != NULL) {
        sprintf_i3_id(remotePublicId_buf, &remotePublicId->id);
    } else {
        sprintf(remotePublicId_buf, "<<NULL>>");
    }

    char contactId_buf[MAX_PRINT_BUF];
    sprintf_i3_id (contactId_buf, &contactId);
   
    char tmp_endPointName [MAX_PRINT_BUF];
    if (isBlank (endPointName)) {
        sprintf (tmp_endPointName, "<<NULL>>");
    } else {
        sprintf (tmp_endPointName, "%s", endPointName);
    }
    
    sprintf(buf,
        "%ld\t" //OCDStateInfo->myHandle,
        "%s\t" // end point name
        "%s\t"  //priv_id
        "%s\t"  //priv trig key   
        "%d\t"  //addr type
        "%d\t"  //nameHashedFlag
        "%s\t"  //remote private id
        "%s\t"  //remote public id
        "%s\t"  //contactId
        "%d"    //i3 VN Type
	,
        myHandle,
        tmp_endPointName,
         prv_id_buf,
         prv_trig_key_buf,
         addr_type,
         nameHashedFlag,
         remotePrivateId_buf,
         remotePublicId_buf,
         contactId_buf,
         i3VNType
        );
}

/**
 * This constructs an i3 virtual node by parsing
 * the specified journal record.  The journal record
 * is assumed to be in the format specified in the
 * function getJournalRecord()
 */
I3OCDStateInfo* I3OCDStateInfo::constructState (char* journalRecordStr, OCI *oci, I3OverlayNetwork* overlay) {
   
    LayerHandle tmp_myHandle;
    int tmp_addr_type;
    int tmp_i3VNType;
    int tmp_nameHashedFlag;
    
    char prv_id_buf[MAX_PRINT_BUF];
    char prv_trig_key_buf[MAX_PRINT_BUF];
    char remotePrivateId_buf[MAX_PRINT_BUF];
    char remotePublicId_buf[MAX_PRINT_BUF];
    char contactId_buf [MAX_PRINT_BUF];
    char tmp_endPointName [MAX_PRINT_BUF];
   
    int numAssigned = sscanf(journalRecordStr,
                                "%ld\t" //OCDStateInfo->myHandle,
                                "%s\t" //end point name
                                "%s\t"  //priv_id
                                "%s\t"  //priv trig key   
                                "%d\t"  //addr type
                                "%d\t"  //nameHashedFlag
                                "%s\t"  //remote private id
                                "%s\t"  //remote public id
                                "%s\t"  //contactId
                                "%d"    //i3 VN Type
	                        ,
                                &tmp_myHandle,
                                tmp_endPointName,
                                prv_id_buf,
                                prv_trig_key_buf ,
                                &tmp_addr_type,
                                &tmp_nameHashedFlag,
                                remotePrivateId_buf,
                                remotePublicId_buf,
                                contactId_buf,
                                &tmp_i3VNType
        );
    
    if (numAssigned < 10) {
	    I3_PRINT_INFO0( INFO_LEVEL_WARNING,
		    "Invalid journal entry being ignored.\n"
		  );
        return NULL;
    }	


    I3OCDStateInfo *newState = new I3OCDStateInfo (tmp_myHandle, oci, overlay);
    
    I3_PRINT_DEBUG5 (DEBUG_LEVEL_SUPER, 
		"Reconstructing virtual node: priv_id=%s"
                "priv_trig_key=%s addr_type=%d remotePrivId=%s remotePubId=%s\n",
		        prv_id_buf, prv_trig_key_buf,
		        tmp_addr_type, remotePrivateId_buf, remotePublicId_buf
	  );
 
    strcpy (newState->endPointName,tmp_endPointName);
    

    newState->addr_type = tmp_addr_type;
    newState->i3VNType = tmp_i3VNType;
    newState->nameHashedFlag = tmp_nameHashedFlag;
    
    sscanf_i3_id(prv_id_buf, &newState->prv_id);    
    sscanf_i3_key(prv_trig_key_buf, &newState->prv_trig_key);    

    sscanf_i3_id(contactId_buf, &newState->contactId);    
    
    if (strcmp(remotePublicId_buf,"<<NULL>>") != 0) {
        ID tmp_remotePublicId;
        sscanf_i3_id(remotePublicId_buf, &tmp_remotePublicId);
        newState->remotePublicId = new I3Id();
        init_i3_id(&newState->remotePublicId->id, &tmp_remotePublicId);
        newState->remotePublicIdStack.ids = &newState->remotePublicId->id;
        newState->remotePublicIdStack.len = 1;
    } else {
	    newState->remotePublicId = NULL;
    	newState->remotePublicIdStack.ids = NULL;
	    newState->remotePublicIdStack.len = -1;
    }

    // NOTE: We may not make use of the remote private id.
    // We will be requesting a remote private again from the remote end point. 
    if (strcmp(remotePrivateId_buf, "<<NULL>>") != 0) { 
        ID tmp_remotePrivateId;
        sscanf_i3_id(remotePrivateId_buf, &tmp_remotePrivateId);
        newState->remotePrivateId = new I3Id();
        init_i3_id(&newState->remotePrivateId->id, &tmp_remotePrivateId);
	    newState->remotePrivateIdStack.len = 1;
	    newState->remotePrivateIdStack.ids = &newState->remotePrivateId->id;
    } else {
	    newState->remotePrivateId = NULL;
	    newState->remotePrivateIdStack.len = -1;
	    newState->remotePrivateIdStack.ids = NULL;
    }
    
    return newState;
}

/**
  * This function is called when the connection associated with this i3 node
  * could not be set up.
  */
void I3OCDStateInfo::connectionFailed() {
            
    I3OverlayNetwork *i3Overlay = (I3OverlayNetwork *) overlay;
    
    // Send NULL DNS reply to any waiting legacy applications.
    notifySetupFailure();

    // destroy this virtual node.
    //i3Overlay->destroyVN(this); //QQQ: Is delete(this) dangerous?


}

